// **************************************************************
// Com ++ Framework by the C++ World
// Please report defects: mailto:compp@cplusplusworld.com
// Please visit: http://www.cplusplusworld.com/socketlibrary.html
// **************************************************************

#include <arpa/inet.h>
#include "ComPlusPlus/ASrv.h"
#include "SysPlusPlus/Tools.h"
#include "SysPlusPlus/Logger.h"

#include "Sig.h"

compp::ASrv::~ASrv () {
 
}
compp::ASrv::ASrv () {

}

compp::ASrv::ASrv (const int p) {

  setPort ( p );
  setIPAddr ( "0.0.0.0" );

}

compp::ASrv::ASrv (const std::string & IPAddr, const int p) {

  setPort ( p );
  setIPAddr ( IPAddr );

}

void compp::ASrv::Broadcast (const bool BC ) {
  Server.SetSockOptBroadcast( BC ) ;
}

void compp::ASrv::AddMultiCastGroup (const std::string & IPAddr ) {
  Server.SetSockOptJoinMulticastGroup ( IPAddr ) ;
}

void compp::ASrv::commenceWork () {
  syspp::Logger *Log = syspp::Logger::Instance();

  Log->Info ("ASR Instance commencing main loop.");

  Server.Bind (IPAddr, port) ;
  
  Server.AttachFD();
  Server.SetSockOptReuseAddress ( true);
  Server.SetLingerTime(0);

  Log->Info ("ASR Instance starting hread.");
  this->Start ( (void *) &Server );
  this->Join();

}

// Protocol: LSEC : LI : SIGNATURE
// LSEC:   CYP| SEC
// LI  : Packetlength over all
// SIGNATURE any value returned as it is
void *compp::ASrv::Run (void*v) {

  syspp::Logger *Log = syspp::Logger::Instance();

  compp::SocketUdp  * worker = (compp::SocketUdp  *) v;
  if ( worker == NULL ){
    Log->Notice ("ASR Instance: Instance == NULL in Run()" );
    return NULL;
}
  int LRequest, LSEC, SIGNATURE, lenraw, port, SECRAW;
  std::string retval, request, raw, host;
  
  ASrvContext Context;
  
#define MAXUDPPacket 65500

  try {
    do {
      
      
      lenraw = worker->Read ( raw, host, port ) ;
      Log->Debug ("ASR Instance: lenraw: %d", lenraw );

      if ( lenraw < 12 ) {
	Log->Notice ("ASR Instance: aborting processing because data is unsuitable(1)" );
	continue;
      }

      if ( (unsigned int ) lenraw != raw.size()  ) {
	Log->Notice ("ASR Instance: aborting processing because data is unsuitable(2)" );
	continue;
      }

      
      raw.copy ( (char *) &LSEC, 4, 0 );
      SECRAW = LSEC;
      raw.copy ( (char *) &LRequest, 4, 4 );
      raw.copy ( (char *) &SIGNATURE, 4, 8 );
      
      LSEC =  ntohl ( LSEC );
      if (LSEC != _COMM_SEC && LSEC != _COMM_CYPH) {
	Log->Notice ("ASR Instance: aborting processing because data is unsuitable(3)" );
        return NULL;
      }
      
      LRequest = ntohl ( LRequest );

      Log->Debug ("ASR Instance: rawsize: %d LRequest: %d", raw.size(), LRequest );
      if ( LRequest < 0 ) {
	Log->Notice ("ASR Instance: aborting processing because data is unsuitable(4)" );
        return NULL;
      }
	

      if ( lenraw != LRequest ) {
	Log->Notice ("ASR Instance: aborting processing because data is unsuitable(5)" );
        return NULL;
      }

      request = raw.substr (  12, lenraw -12  );
      Context.SetContextString ( worker->GetContextString());

      if (LSEC != _COMM_CYPH) {
	Log->Debug ("ASR Instance: Data is of type 1" );
	syspp::Tools::Dec (request);
      } else {
	Log->Debug ("ASR Instance: Data is of type 2" );
      }

      retval = executeTransaction  ( request, Context  )  ;

      // Cyphering
      if (LSEC == _COMM_CYPH) {
	syspp::Tools::Enc ( retval );
      }

      std::string R = "";
      // SEC
      char c[sizeof (int)]; // Temporary
      memcpy ( (void*) c, (void*) &SECRAW, sizeof (SECRAW));
      R.append ( c, sizeof (SECRAW));

      // LI
      int Len = htonl ( retval.size() + 12 );
      memcpy ( (void*) c, (void*) &Len, sizeof (Len));
      R.append ( c, sizeof ( Len ) );

      // SIGNATURE
      memcpy ( (void*) c, (void*) &SIGNATURE, sizeof (SIGNATURE));
      R.append ( c, sizeof ( SIGNATURE ) );

      R.append ( retval );

      if ( worker->Sendto ( R.data(), R.size(), host, port ) < 0 ) {
	Log->Notice ("ASR Instance: Cannot send data errno = %d", errno );
	return NULL;
      }

   } while ( true ) ;
  } catch ( syspp::ComException & e ) {
	Log->Notice ("ASR Instance Exception: %s", e.what () );
  } catch ( ... ) {
	Log->Notice ("ASR Instance Exception: other" );

  }
  return NULL;
}

std::string  compp::ASrv::executeTransaction (  const std::string & request, ASrvContext & Context ) { 
  std::string ret = "ASRvTest ";
  ret += Context.GetContextString();
  return ret ;
} 
